/**
 * defineClass( ) -- a utility function for defining JavaScript classes. 定义类的工具函数
 *
 * 此函数只接受单个对象参数  函数的返回值是类的构造函数 
 * 此函数处理一系列创建一个类的步骤 包括设置prototype对象 拷贝方法 等等
 * 
 * This function expects a single object as its only argument.  It defines
 * a new JavaScript class based on the data in that object and returns the
 * constructor function of the new class.  This function handles the repetitive
 * tasks of defining classes: setting up the prototype object for correct
 * inheritance, copying methods from other types, and so on.
 *
 * The object passed as an argument should have some or all of the
 * following properties: 
 * 传递的参数对象应该包含以下属性
 * 
 *      name: The name of the class being defined. 
 *            If specified, this value will be stored in the classname
 *            property of the prototype object. 
 *            创建的类的名称 一旦指定类名 则创建prototype对象的classname属性
 *            
 *		
 *    extend: The constructor of the class to be extended. If omitted,
 *            the Object( ) constructor will be used. This value will
 *            be stored in the superclass property of the prototype object.
 *            需要扩展的类的构造函数 默认是Object的构造函数
 *
 *		
 * construct: The constructor function for the class. If omitted, a new
 *            empty function will be used. This value becomes the return
 *            value of the function, and is also stored in the constructor
 *            property of the prototype object. 
 *            类的构造函数 默认创建空函数
 *
 *		
 *   methods: An object that specifies the instance methods (and other shared
 *            properties) for the class. The properties of this object are
 *            copied into the prototype object of the class. If omitted,
 *            an empty object is used instead. Properties named
 *            "classname", "superclass", and "constructor" are reserved
 *            and should not be used in this object.
 *            为类定义的实例方法(以及共享的属性) 这些属性都将被拷贝到类的prototype对象 默认创建空对象
 *			  PS：classname, superclass, constructor都应该保留 不被使用
 *
 *		
 *   statics: An object that specifies the static methods (and other static
 *            properties) for the class. The properties of this object become
 *            properties of the constructor function. If omitted, an empty
 *            object is used instead.
 *            为类定义的静态方法(以及静态属性) 这些属性都将是构造函数的属性 默认创建空对象
 *
 *		
 *   borrows: A constructor function or array of constructor functions.
 *            The instance methods of each of the specified classes are copied
 *            into the prototype object of this new class so that the 
 *            new class borrows the methods of each specified class.
 *            Constructors are processed in the order they are specified,
 *            so the methods of a class listed at the end of the array may
 *            overwrite the methods of those specified earlier. Note that
 *            borrowed methods are stored in the prototype object before
 *            the properties of the methods object above. Therefore,
 *            methods specified in the methods object can overwrite borrowed
 *            methods. If this property is not specified, no methods are
 *            borrowed.
 *            构造函数或是构造函数的数组 指定的类的实例方法都将被拷贝到类的prototype对象
 *
 *		
 *  provides: A constructor function or array of constructor functions.
 *            After the prototype object is fully initialized, this function
 *            verifies that the prototype includes methods whose names and
 *            number of arguments match the instance methods defined by each
 *            of these classes. No methods are copied; this is simply an
 *            assertion that this class "provides" the functionality of the
 *            specified classes. If the assertion fails, this method will
 *            throw an exception. If no exception is thrown, any
 *            instance of the new class can also be considered (using "duck
 *            typing") to be an instance of these other types.  If this
 *            property is not specified, no such verification is performed.
 *            构造函数或是构造函数的数组 当prototype对象初始完毕后 
 *			  将会检查prototype对象包含与定义的类的实例方法相匹配的方法 相对于类必须实现的方法
 *            
 **/

function defineClass(data) {
	// Extract the fields we'll use from the argument object.
	// Set up default values. 读取data对象 设置默认值
	var classname = data.name;
	var superclass = data.extend || Object;
	var constructor = data.construct || function() {};
	var methods = data.methods || {};
	var statics = data.statics || {};
	var borrows;
	var provides;
	// Borrows may be a single constructor or an array of them.
	if (!data.borrows)
		borrows = [];
	else if (data.borrows instanceof Array)
		borrows = data.borrows;
	else
		borrows = [data.borrows];
	// Ditto for the provides property.
	if (!data.provides)
		provides = [];
	else if (data.provides instanceof Array)
		provides = data.provides;
	else
		provides = [data.provides];
	// Create the object that will become the prototype for our class.
	var proto = new superclass();
	// Delete any noninherited properties of this new prototype object.
	for (var p in proto)
		if (proto.hasOwnProperty(p))
			delete proto[p];
	// Borrow methods from "mixin" classes by copying to our prototype.
	for (var i = 0; i < borrows.length; i++) {
		var c = data.borrows[i];
		borrows[i] = c;
		// Copy method properties from prototype of c to our prototype
		for (var p in c.prototype) {
			if (typeof c.prototype[p] != "function")
				continue;
			proto[p] = c.prototype[p];
		}
	}
	// Copy instance methods to the prototype object
	// This may overwrite methods of the mixin classes
	for (var p in methods)
		proto[p] = methods[p];
	// Set up the reserved "constructor", "superclass", and "classname"
	// properties of the prototype.
	proto.constructor = constructor;
	proto.superclass = superclass;
	// classname is set only if a name was actually specified.
	if (classname)
		proto.classname = classname;
	// Verify that our prototype provides all of the methods it is supposed to.
	for (var i = 0; i < provides.length; i++) { // for each class
		var c = provides[i];
		for (var p in c.prototype) { // for each property
			if (typeof c.prototype[p] != "function")
				continue; // methods only
			if (p == "constructor" || p == "superclass")
				continue;
			// Check that we have a method with the same name and that
			// it has the same number of declared arguments. If so, move on
			if (p in proto && typeof proto[p] == "function" && proto[p].length == c.prototype[p].length)
				continue;
			// Otherwise, throw an exception
			throw new Error("Class " + classname + " does not provide method " + c.classname + "." + p);
		}
	}
	// Associate the prototype object with the constructor function
	constructor.prototype = proto;
	// Copy static properties to the constructor
	for (var p in statics)
		constructor[p] = data.statics[p];
	// Finally, return the constructor function
	return constructor;
}

/* ---------------------------	Using the defineClass( ) method	------------------------- */

// A Comparable class with an abstract method
// so that we can define classes that "provide" Comparable.
var Comparable = defineClass({
			name	: "Comparable",
			methods	: {
				compareTo	: function(that) {
					throw "abstract";
				}
			}
		});

// A mixin class with a usefully generic equals( ) method for borrowing
var GenericEquals = defineClass({
			name	: "GenericEquals",
			methods	: {
				equals	: function(that) {
					if (this == that)
						return true;
					var propsInThat = 0;
					for (var name in that) {
						propsInThat++;
						if (this[name] !== that[name])
							return false;
					}
					// Now make sure that this object doesn't have additional props
					var propsInThis = 0;
					for (name in this)
						propsInThis++;
					// If this has additional properties, then they are not equal
					if (propsInThis != propsInThat)
						return false;
					// The two objects appear to be equal.
					return true;
				}
			}
		});

// A very simple Rectangle class that provides Comparable
var Rectangle = defineClass({
			name		: "Rectangle",
			construct	: function(w, h) {
				this.width = w;
				this.height = h;
			},
			methods		: {
				area		: function() {
					return this.width * this.height;
				},
				compareTo	: function(that) {
					return this.area() - that.area();
				}
			},
			provides	: Comparable
		});

// A subclass of Rectangle that chains to its superclass constructor,
// inherits methods from its superclass, defines an instance method and
// a static method of its own, and borrows an equals( ) method.
var PositionedRectangle = defineClass({
			name		: "PositionedRectangle",
			extend		: Rectangle,
			construct	: function(x, y, w, h) {
				this.superclass(w, h); // chain to superclass
				this.x = x;
				this.y = y;
			},
			methods		: {
				isInside	: function(x, y) {
					return x > this.x && x < this.x + this.width && y > this.y && y < this.y + this.height;
				}
			},
			statics		: {
				comparator	: function(a, b) {
					return a.compareTo(b);
				}
			},
			borrows		: [GenericEquals]
		});
